/*
 * Copyright (c) 2010 Frederic Daoud
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.fdaoud.rayures.extension;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Locale;
import javax.annotation.Resource;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import net.sourceforge.stripes.controller.StripesFilter;
import net.sourceforge.stripes.format.Formatter;
import net.sourceforge.stripes.config.TargetTypes;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.validation.TypeConverter;
import net.sourceforge.stripes.validation.ValidationError;
import com.fdaoud.rayures.repository.Dao;
import org.springframework.stereotype.Component;

@TargetTypes({Entity.class, MappedSuperclass.class})
@Component
public class JpaEntityConverter implements Formatter<Object>, TypeConverter<Object> {
  private static final Log log = Log.getInstance(JpaEntityConverter.class);

  private Locale locale;

  @Resource
  private Dao dao;

  public String format(Object object) {
    Class cls = object.getClass();
    String className = cls.getSimpleName();
    // TODO: maybe use net.sf.cglib.proxy.Proxy.isProxyClass(cls) and proxy.Enhancer.isEnhanced(cls) instead

    int index = className.indexOf("$$");
    if (index != -1) {
      className = className.substring(0, index);
    }

    // TODO: expects id in getXyId(); would be better to detect @Id
    String getter = "get" + className + "Id";

    try {
      Method method = cls.getMethod(getter, (Class[]) null);
      Object id = method.invoke(object, (Object[]) null);
      return String.valueOf(id);
    }
    catch (Exception exc) {
      throw new RuntimeException("Could not invoke " + getter + "() on " + object
        + " of class " + object.getClass().getName(), exc);
    }
  }

  public Object convert(String input, Class<?> type, Collection<ValidationError> errors) {
    log.debug("convert: ", input, type);

    if (input == null) {
      log.debug("returning empty object");
      return emptyObject(type);
    }
    try {
      String getter = "get" + type.getSimpleName() + "Id";
      log.debug("getter: ", getter);
      Method method = type.getMethod(getter, (Class[]) null);
      Class<?> returnType = method.getReturnType();
      log.debug("rt: ", returnType);
      // TODO: need to convert input to type of id property
      TypeConverter tc = StripesFilter.getConfiguration().getTypeConverterFactory()
        .getTypeConverter(returnType, locale);
      log.debug("tc: ", tc);
      Object id = tc.convert(input, returnType, errors);
      log.debug("id: ", id);

      Object result = dao.findById(type, id);
      log.debug("result: ", result);
      if (result == null) {
        return emptyObject(type);
      }
      return result;
    }
    catch (Exception exc) {
      // TODO: this is too general
      log.error(exc);
      return emptyObject(type);
    }
  }

  protected Object emptyObject(Class<?> type) {
    try {
      return type.newInstance();
    }
    catch (Exception exc) {
      throw new RuntimeException("Could not create an instance of " + type.getClass().getName(), exc);
    }
  }

  public void init() { }
  public void setFormatPattern(String formatPattern) { }
  public void setFormatType(String formatType) { }
  public void setLocale(Locale locale) { this.locale = locale; }
}
